package db

import (
	"database/sql"
	"errors"
	_ "github.com/go-sql-driver/mysql"
	"log"
	"os"
	"reflect"
)

var myDb *sql.DB

func InitDBPool(driverClassName /*数据库类型*/, dns /*连接字符串*/ string, poolSize, maxIdleConn int) {
	var err error
	myDb, err = sql.Open(driverClassName, dns)
	if err != nil {
		log.Println(err.Error())
		os.Exit(1)
	}
	myDb.SetMaxOpenConns(poolSize)
	myDb.SetMaxIdleConns(maxIdleConn)
	myDb.Ping()
}

func Close() {
	myDb.Close()
}

func Raw(query string, args ...interface{}) RawSeter {
	o := NewRawSet(query, args)
	return o
}

// Item
type Item map[string]interface{}

type rawSet struct {
	sql  string
	args []interface{}
}

// raw query seter
type RawSeter interface {
	Insert() (int64, error)
	Exec() (int64, error)
	FetchRow(interface{}, map[string]string) error
	FetchRows(interface{}, map[string]string) (int64, error)
	//   SetArgs(...interface{}) RawSeter
}

func NewRawSet(query string, args []interface{}) RawSeter {
	o := new(rawSet)
	o.sql = query
	o.args = args
	return o
}

//插入
func (r *rawSet) Insert() (int64, error) {
	stmtIns, err := myDb.Prepare(r.sql)
	if err != nil {
		panic(err.Error())
	}
	defer stmtIns.Close()

	result, err := stmtIns.Exec(r.args...)
	if err != nil {
		panic(err.Error())
	}
	return result.LastInsertId()
}

//修改和删除
func (r *rawSet) Exec() (int64, error) {
	stmtIns, err := myDb.Prepare(r.sql)
	if err != nil {
		panic(err.Error())
	}
	defer stmtIns.Close()

	result, err := stmtIns.Exec(r.args...)
	if err != nil {
		panic(err.Error())
	}
	return result.RowsAffected()
}

//读取一行
//prt 为  &struct  &Item  和  &Slice
func (r *rawSet) FetchRow(ptr interface{}, mapping map[string]string) (error) {
	rows, columns, err := rows(myDb, r.sql, r.args)
	defer rows.Close()
	columnsLen := len(columns)
	kind, ptrRow, scan, err := scanVariables(ptr, mapping, columns, false)
	if err != nil {
		return err
	}
	val := reflect.ValueOf(ptr).Elem()
	defer rows.Close()
	if rows.Next() {
		err = rows.Scan(scan...)
		if err != nil {
			return err
		}
		switch kind {
		case reflect.Struct: // struct
			val.Set(reflect.ValueOf(ptrRow).Elem())
		case reflect.Map: //map
			row := make(map[string]interface{}, columnsLen)
			for i := 0; i < columnsLen; i++ {
				row[columns[i]] = typeAssertion(*(scan[i].(*interface{})))
			}
			val.Set(reflect.ValueOf(row))
		case reflect.Slice: //slice
			row := make([]interface{}, columnsLen)
			for i := 0; i < columnsLen; i++ {
				row[i] = typeAssertion(*(scan[i].(*interface{})))
			}
			val.Set(reflect.ValueOf(row))
		}
	} else {
		return errors.New("not rows found")
	}
	if err = rows.Err(); err != nil {
		return err
	}
	return nil
}

//读取多行
//prt 为  &[]struct  &[]Item  和  &[]Slice
func (r *rawSet) FetchRows(ptr interface{}, mapping map[string]string) (int64, error) {
	rows, columns, err := rows(myDb, r.sql, r.args)
	if err != nil {
		panic(err.Error())
		return 0, err
	}
	defer rows.Close()
	columnsLen := len(columns)
	kind, ptrRow, scan, err := scanVariables(ptr, mapping, columns, true)
	if err != nil {
		panic(err.Error())
		return 0, err
	}

	val := reflect.ValueOf(ptr).Elem()
	var rowNum int64
	for rows.Next() {
		if err := rows.Scan(scan...); err != nil {
			panic(err.Error())
			return 0, err
		}
		switch kind {
		case reflect.Struct:
			val.Set(reflect.Append(val, reflect.ValueOf(ptrRow).Elem()))
		case reflect.Map:
			row := make(map[string]interface{}, columnsLen)
			for i := 0; i < columnsLen; i++ {
				row[columns[i]] = typeAssertion(*(scan[i].(*interface{})))
			}
			val.Set(reflect.Append(val, reflect.ValueOf(row)))
		case reflect.Slice:
			row := make([]interface{}, columnsLen)
			for i := 0; i < columnsLen; i++ {
				row[i] = typeAssertion(*(scan[i].(*interface{})))
			}
			val.Set(reflect.Append(val, reflect.ValueOf(row)))
		}
		rowNum++
	}
	if err = rows.Err(); err != nil {
		panic(err.Error())
		return 0, err
	}
	return rowNum, nil
}

func rows(db *sql.DB, sqlStr string, args []interface{}) (*sql.Rows, []string, error) {
	stmtOut, err := db.Prepare(sqlStr)
	if err != nil {
		panic(err.Error())
	}
	defer stmtOut.Close()
	rows, err := stmtOut.Query(args...)
	if err != nil {
		panic(err.Error())
	}
	columns, err := rows.Columns()
	if err != nil {
		panic(err.Error())
	}
	return rows, columns, nil
}

func scanVariables(ptr interface{}, mapping map[string]string, columns []string, isRows bool) (reflect.Kind, interface{}, []interface{}, error) {
	columnsLen := len(columns)
	typ := reflect.ValueOf(ptr).Type()

	if typ.Kind() != reflect.Ptr {
		return 0, nil, nil, errors.New("ptr is not a pointer")
	}
	elemTyp := typ.Elem()

	if isRows {
		if elemTyp.Kind() != reflect.Slice {
			return 0, nil, nil, errors.New("ptr is not point a slice")
		}
		elemTyp = elemTyp.Elem()
	}

	elemKind := elemTyp.Kind()
	scan := make([]interface{}, columnsLen)
	if elemKind == reflect.Struct {
		row := reflect.New(elemTyp) // Data
		//columns2fieldMp := make(map[string]string, row.Elem().Type().NumField())
		//columnNameMap := make(map[string]string, columnsLen)
		//for _, v := range columns {
		//	columnNameMap[strings.ToLower(v)] = v
		//}
		//for j := 0; j < elemTyp.NumField(); j++ {
		//	s := elemTyp.Field(j).Name
		//	if v, found := columnNameMap[strings.ToLower(s)]; found {
		//		columns2fieldMp[v] = s
		//	}
		//}
		for i := 0; i < columnsLen; i++ {
			if row.Elem().FieldByName(mapping[columns[i]]).IsValid() {
				scan[i] = row.Elem().FieldByName(mapping[columns[i]]).Addr().Interface()
			} else {
				scan[i] = new(interface{})
			}
		}
		return elemKind, row.Interface(), scan, nil
	}

	if elemKind == reflect.Map || elemKind == reflect.Slice {
		row := make([]interface{}, columnsLen) // Data
		for i := 0; i < columnsLen; i++ {
			scan[i] = &row[i]
		}
		return elemKind, &row, scan, nil
	}
	return 0, nil, nil, errors.New("ptr is not a point struct, map or slice")
}

func typeAssertion(v interface{}) interface{} {
	switch v.(type) {
	case bool:
		return v.(bool)
	case int64:
		return v.(int64)
	case float64:
		return v.(float64)
	case string:
		return v.(string)
	case []byte:
		return string(v.([]byte))
	default:
		return ""
	}
}
